/**
 * 
 */
package it.unitn.testbed.server.task;

import it.unitn.testbed.server.TestbedServiceImpl;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.log4j.Logger;

/**
 * A task scheduler.
 * 
 * The task scheduler must be a singleton instance to prevent several task of
 * running at the same time. Use {@link #getScheduler()} to obtain a reference
 * to the singleton.
 * 
 * To schedule tasks for execution, call
 * {@link #addTask(ArrayList, HashMap, int, TestbedServiceImpl)}.
 * 
 * Optionally, other events (for instance, task cleanup) can be scheduled using
 * {@link #scheduleEvent(TimerTask, long)}.
 * 
 * @author Stefan Guna
 * 
 */
public class TaskScheduler {
	private static final long INTER_TASK = 10000;

	public static Logger log = Logger.getLogger(TaskScheduler.class.getName());
	private static TaskScheduler singleton;

	/**
	 * Gets a reference to the singleton object.
	 * 
	 * @return A singleton object.
	 */
	public synchronized static TaskScheduler getScheduler() {
		if (singleton == null)
			singleton = new TaskScheduler();
		return singleton;
	}

	/** The next execution of a task. */
	private Date nextExecution;
	/** Used internally to schedule tasks. */
	private Timer timer;

	private TaskScheduler() {
		timer = new Timer(true);
		nextExecution = new Date();
	}

	/**
	 * Adds a new task to be executed.
	 * 
	 * @param components
	 *            The task components, denoting mappings binary - mote IDs.
	 * @param serialMsgs
	 *            The serial message classes loaded for receiving messages.
	 * @param duration
	 *            The duration of the task.
	 * @param testbedServiceImpl
	 *            Callback object.
	 * @return the time when this task is scheduled for execution.
	 */
	public synchronized Date addTask(ArrayList<TaskComponent> components,
			HashMap<String, File> serialMsgs, int duration,
			TestbedServiceImpl testbedServiceImpl) {
		Task task = new Task(components, serialMsgs, duration,
				testbedServiceImpl, this);
		long scheduled;
		if (nextExecution.before(new Date()))
			scheduled = Calendar.getInstance().getTimeInMillis() + INTER_TASK;
		else {
			Calendar nextCalendar = Calendar.getInstance();
			nextCalendar.setTime(nextExecution);
			scheduled = nextCalendar.getTimeInMillis();
		}

		Date scheduledDate = new Date(scheduled);
		timer.schedule(task, scheduledDate);

		long nextTask = scheduled + duration * 60000 + INTER_TASK;
		nextExecution = new Date(nextTask);
		log.info("Task scheduled for " + scheduledDate);
		return scheduledDate;
	}

	/**
	 * Schedules an event for execution.
	 * 
	 * @param task
	 *            The event to be scheduled.
	 * @param delay
	 *            The delay after which the event should be fired.
	 */
	public void scheduleEvent(TimerTask task, long delay) {
		timer.schedule(task, delay);
	}
}
